Repository navigation
fix(mindrecord): MEMORIES 카드의 '그날의 기록 다시 읽기' 죽은 영역을 없앤다 (#793) - #1105
Conversation
목적지가 미확정이라 onReadAgainClick 을 비워 뒀는데, 버튼이 자체 clickable 이라 클릭을 삼켰다. 결과는 «카드 여백을 누르면 추억 공간이 열리고, 정작 가장 눌러 보고 싶은 버튼만 아무 일도 안 한다» 였다. 새 목적지를 지어내지 않는다 — 이미 동작하는 카드 자신의 목적지를 물려준다. 확정이 오면 호출부의 인자 하나만 바꾸면 된다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1hyok
left a comment
There was a problem hiding this comment.
죽은 영역 진단은 맞고, «새 목적지를 지어내는 게 아니라 이미 동작하는 카드의 목적지를 물려준다» 는 논리도 이슈 #793 의 후보 (b)(추억 공간)와 어긋나지 않는다. 방향에는 이견 없다.
다만 CI 가 red 이고 원인이 이 PR 이 추가한 테스트 코드라 그대로는 머지되지 않는다.
검증
- CI:
Check Project Issues (Android Lint)fail. 스텝 단위로 특정했다 — 실패한 것은 7번Run Android Lint이고, 8번 merge·9번Report Android Lint (Reviewdog)는 성공했다. 즉 reviewdog 인프라 문제가 아니라 코드에서 나온 lint error 다. 나머지 10종은 green. - 실패 원문:
MemoriesReadAgainTest.kt:37: Error: Calling a @RememberInComposition annotated declaration inside composition without using remember [RememberInComposition]—Lint found 1 error, 3 warnings→Lint found errors in the project; aborting build. - 시안 대조 해당 없음 — 이슈 #793 본문대로 Figma 카드 노드
109:15028의 이 버튼에는 프로토타입 연결이 없어, 시안이 목적지를 판정해 주지 못한다. 실기동 QA 해당 없음 — 클릭 전달 경로를 새 Robolectric 테스트가 덮는다(단, 그 테스트가 아래 blocking 의 당사자다).
지적
- [blocking] 새 테스트가 Android Lint 를 깨뜨린다 →
MemoriesReadAgainTest.kt:37인라인 코멘트. - [normal] 종전 «목적지를 비워 둔다» 결정을 뒤집으면서 이슈에 근거를 남기지 않았다 →
HomeTabMindRecordLazyItems.kt:152인라인 코멘트. - [normal] 테스트를 위한 가시성 완화 →
HomeTabMindRecordLazyItems.kt:121인라인 코멘트.
| MemoriesSectionContent( | ||
| onMemoriesSectionClick = { clicks += 1 }, | ||
| clickLabel = "추억 공간 열기", | ||
| interactionSource = MutableInteractionSource(), |
There was a problem hiding this comment.
[blocking] 이 줄이 CI 의 Android Lint 를 깨뜨린다.
MemoriesReadAgainTest.kt:37: Error: Calling a @RememberInComposition annotated declaration
inside composition without using remember [RememberInComposition from androidx.compose.runtime]
MutableInteractionSource() 는 @RememberInComposition 이 붙어 있어 setContent { } 안에서 맨손으로 호출할 수 없다. 컴포지션마다 새 인스턴스가 생겨 상호작용 상태가 유실되는 것을 막으려는 규칙이다.
둘 중 아무 쪽이나 통한다.
// (a) 컴포지션 밖에서 만들어 넘긴다
val interactionSource = MutableInteractionSource()
composeRule.setContent {
AfternoteTheme {
MemoriesSectionContent(interactionSource = interactionSource, ...)
}
}// (b) 컴포지션 안이라면 remember 로 감싼다
interactionSource = remember { MutableInteractionSource() },이 한 건 때문에 Lint found errors in the project; aborting build 로 잡 전체가 중단된다 — 고치면 red 도 함께 풀린다.
| MemoriesCard( | ||
| question = question, | ||
| answer = answer, | ||
| onReadAgainClick = onMemoriesSectionClick, |
There was a problem hiding this comment.
[normal] 이 인자를 비워 둔 것은 실수가 아니라 PR #721 리뷰에서 합의한 결정이었고, 이슈 #793 이 그 이유를 «확정 전까지 임의 목적지를 붙이지 않는 이유는 미확정 탐색 동작이 제품 동작으로 굳는 것을 막기 위해서다» 라고 명시적으로 기록해 두었다.
이 PR 의 논리(죽은 영역 > 미확정 보류, 그리고 새 목적지가 아니라 컨테이너 목적지의 승계)에는 동의한다. 다만 결정이 뒤집힌 사실이 이슈에는 남지 않는다 — 지금 상태로 머지되면 #793 은 여전히 «목적지 미확정, 무동작» 이라고 적혀 있고, 나중에 이 코드를 보는 사람은 종전 합의를 모른 채 되돌릴 수 있다.
같은 저자의 #1103 은 이 상황을 docs/qa/assumptions.md 에 «판단 보류» 로 남겨 처리했다. 여기도 같은 방식이면 충분하다 — 이슈 코멘트든 assumptions.md 항목이든, «최종 목적지는 여전히 미확정이며 그때까지 카드 목적지를 승계한다» 가 코드 밖에 한 줄 남으면 된다.
|
|
||
| @Composable | ||
| private fun MemoriesSectionContent( | ||
| internal fun MemoriesSectionContent( |
There was a problem hiding this comment.
[normal] private → internal 완화의 유일한 이유가 테스트 접근으로 보인다. 이 정도는 이 레포에서 흔한 타협이라 blocking 으로 올리지 않지만, 모듈 공개 표면이 한 칸 넓어지는 것은 사실이다.
homeTabMindRecordMemoriesSection(이미 public 인 LazyListScope 확장) 쪽에서 같은 클릭 경로를 태울 수 있다면 가시성을 되돌릴 수 있고, 그게 어렵다면 @VisibleForTesting 을 붙여 의도를 표시해 두는 편이 낫다.
|
실기동 확인 — 서버가 복구돼 태워 봤습니다. 홈 → MEMORIES 카드 → 「그날의 기록 다시 읽기」 탭 → 추억 공간(MEMORY SPACE) 진입을 확인했습니다. 수정 전에는 이 버튼만 클릭을 삼켜 아무 일도 일어나지 않았고, 카드 여백을 눌러야 같은 화면이 열렸습니다. 이제 둘이 같은 곳으로 갑니다. develop 을 물려 최신 상태로 맞춰 두었습니다. |
MutableInteractionSource 는 @RememberInComposition 이라 컴포지션 안에서 맨손으로 만들 수 없다 — 컴포지션 밖에서 만들어 넘긴다. 테스트 접근을 위해 넓힌 가시성에는 @VisibleForTesting 을 붙여 의도를 표시하고, 목적지 승계가 «종전 결정을 뒤집은 것이 아니라 구멍을 메운 것» 이라는 근거를 docs/qa/assumptions.md 에 남긴다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
지적 3건 모두 반영했습니다. 1. Android Lint red — 원인 특정이 정확했습니다. // MutableInteractionSource 는 @RememberInComposition 이라 컴포지션 안에서 맨손으로
// 만들 수 없다 — 컴포지션마다 새 인스턴스가 생겨 상호작용 상태가 유실된다.
val interactionSource = MutableInteractionSource()
composeRule.setContent { ... interactionSource = interactionSource ... }
2. 결정이 뒤집힌 사실이 코드 밖에 안 남는다 — 3. 테스트를 위한 가시성 완화 — 추가로 실기동 확인도 했습니다 (서버 복구 후): |
1hyok
left a comment
There was a problem hiding this comment.
지적 3건 모두 닫혔고 red 도 함께 풀렸습니다. 승인합니다.
지적 처리
| # | 지적 | 처리 |
|---|---|---|
| 1 | [blocking] 새 테스트가 Android Lint 를 깨뜨린다 | 닫힘 — CI green |
| 2 | [normal] 결정이 뒤집힌 사실이 코드 밖에 안 남는다 | 닫힘 — docs/qa/assumptions.md |
| 3 | [normal] 테스트를 위한 가시성 완화 | 닫힘 — @VisibleForTesting |
1번 — MutableInteractionSource() 를 setContent 밖으로 꺼냈습니다. 예상대로 이 한 건이 aborting build 를 걸고 있었고, 지금은 Check Project Issues (Android Lint) 를 포함해 필수 체크 전부 green 입니다. 주석에 «왜 컴포지션 안에서 못 만드는가» 를 적어 둔 것도 좋습니다 — 다음 사람이 한 줄로 되돌리기 쉬운 자리라서요.
2번 — 표현을 «뒤집었다» 가 아니라 «새 목적지를 만들지 않는 선에서 구멍만 메웠다» 로 잡은 것이 실제 성격에 더 맞습니다. 제가 지적한 것보다 정확합니다. 「정리 Screen Design」 페이지에도 이 버튼의 목적지를 정하는 프레임이 없다는 것까지 적혀 있어, 확정이 왔을 때 무엇을 바꾸면 되는지(호출부 인자 하나)가 문서만 봐도 서 있습니다.
3번 — homeTabMindRecordMemoriesSection 쪽으로 태우지 않은 이유에 동의합니다. LazyColumn 을 세우고 hiltViewModel() 을 타는 HomeTabMindRecordMemoriesItem 을 거치면 «클릭이 전달되는가» 를 확인하는 데 Hilt 배선을 더 많이 태우게 되고, 그건 테스트가 검증하려는 것과 다른 것을 깨지게 만듭니다. 의도 표시로 충분합니다.
검증
- CI: 필수 체크 전부 green (head
1ca279c7). 앞 리뷰에서 red 였던Run Android Lint스텝 포함. - develop 병합 실측:
git merge-tree --write-tree origin/develop <head>→ 충돌 0건. develop428022ff기준. - 가시성 점검:
@VisibleForTesting기본값은otherwise = PRIVATE이라 모듈 밖 호출이면 lint 가 잡는데, 프로덕션 호출부(HomeTabMindRecordMemoriesItem)가 같은 파일 안이라 걸리지 않습니다 — lint green 이 그걸 실증합니다. - 실기동 QA: 저자 실측으로 갈음합니다. 「그날의 기록 다시 읽기」 탭 → MEMORY SPACE 진입이 확인됐고, 수정 전에는 이 버튼만 클릭을 삼켰다는 대조까지 붙어 있어 이 PR 이 없애려던 죽은 영역이 실제로 없어진 것이 확인됩니다.
- 시안 대조: 앞 리뷰와 같습니다 — Figma 카드 노드
109:15028의 이 버튼에 프로토타입 연결이 없어 시안이 목적지를 판정해 주지 못합니다. 그래서 «미확정» 이 문서에 남는 쪽이 맞는 처리입니다.
|
이 PR 은
|
assumptions.md 는 문서 끝에 각자 절을 덧붙인 인접 추가라 둘 다 살렸다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
#793 의 증상을 없앤다. 최종 목적지 확정은 여전히 열려 있다 — 아래 「무엇이 남는가」.
무엇이 문제였나
목적지가 미확정이라
onReadAgainClick을 비워 뒀는데, 버튼이 자체clickable이라 클릭을 삼킨다.카드를 감싼
Column은 이미 추억 공간으로 간다(onMemoriesSectionClick→Route.MemorySpace). 그래서 실제 사용자 경험은 «카드 여백을 누르면 추억 공간이 열리는데, 정작 가장 눌러 보고 싶은 버튼만 아무 일도 안 한다» 였다. 비워 두는 선택이 아무 동작 없음이 아니라 카드 안의 구멍이 된 셈이다.무엇을 바꿨나
버튼에 카드 자신의 목적지를 물려준다. 새 목적지를 지어내는 게 아니라, 이미 동작하는 컨테이너와 같은 곳으로 보내 구멍을 메우는 것이다.
확정이 오면 호출부의 이 인자 하나만 바꾸면 된다 — #721 리뷰가 남긴 «호출부에서 인자 하나만 채우면 된다» 구조를 그대로 유지한다.
무엇이 남는가
최종 목적지는 여전히 미확정이다. 후보 (a) 그 기록의 상세 · (b) 추억 공간 · (c) 마인드레코드 목록 중 이 PR 은 (b) 를 임시로 따른다 — 카드 전체가 이미 (b) 로 가고 있어 «버튼만 다른 곳» 이 되지 않기 때문이다.
시안 쪽 근거도 하나 덧붙인다: 「정리 Screen Design」 페이지의 홈 섹션(
4327:99448) 안에 MEMORY SPACE 화면(4327:67706)이 함께 놓여 있다. 여전히 이 버튼에 프로토타입 연결은 없어 확정 근거로는 부족하지만, (b) 가 (a)·(c) 보다 배치상 가깝다.회귀 가드
MemoriesReadAgainTest— 버튼을 실제로 눌러 카드와 같은 콜백이 도는지 확인한다. 인자를 도로 빼면 실패한다.검증
testDebugUnitTest:app:assembleDebug실기동 QA: 못 했다. dev 서버가 점검 시간대라 홈이 «정보를 불러오지 못했습니다» 로 뜬다(에뮬레이터 확인). 서버가 열리면 홈 → MEMORIES 카드 버튼 → 추억 공간 진입을 확인해 코멘트로 남기겠다. 대신 Robolectric 으로 실제 컴포지션에서 버튼을 눌러 콜백을 확인했다.